using Godot;
using System.Collections.Generic;

public partial class Player : CharacterBody2D, StateMachineOwner
{
    private const float runSpeed = 160;
    private const float floor_acceleration = runSpeed / 0.2f;
    private const float air_acceleration = runSpeed / 0.02f;
    private const float jumpVelocity = -320;
    private List<int> GROUD_STATE = new(){ (int)State.IDLE, (int)State.RUNNING };
    private float defaultGravity = (float)ProjectSettings.GetSetting("physics/2d/default_gravity");
    [Export]
    public AnimationPlayer animationPlayer;
    [Export]
    public Sprite2D sprite2D;
    [Export]
    public Timer coyoteTimer;
    [Export]
    public Timer jumpRequestTimer;

    enum State
    {
        IDLE,
        RUNNING,
        JUMP,
        FALL,
        LANDING,
    }

    bool isFirstFrame = false;

    public override void _UnhandledInput(InputEvent @event)
    {
        if (@event.IsActionPressed("jump"))
        {
            jumpRequestTimer.Start();
        }
        if (@event.IsActionReleased("jump"))
        {
            jumpRequestTimer.Stop();
            if (Velocity.Y < jumpVelocity / 2)
            {
                var vel = Velocity;
                vel.Y = jumpVelocity / 2;
                Velocity = vel;
            }
        }
    }

    public void Move(float gravity,double delta)
    {
        var vel = Velocity;
        // 左右方向
        var dir = Input.GetAxis("move_left", "move_right");
        var acceleration = IsOnFloor() ? floor_acceleration : air_acceleration;
        vel.X = Mathf.MoveToward(vel.X, dir * runSpeed, acceleration * (float)delta);
        // 重力加速度
        vel.Y += gravity * (float)delta;
        Velocity = vel;
        //左右水平翻转
        if (!Mathf.IsZeroApprox(dir))
        {
            sprite2D.FlipH = dir < 0;
        }

        MoveAndSlide();
    }

    public void Stand(float gravity, double delta)
    {
        var vel = Velocity;
        var acceleration = IsOnFloor() ? floor_acceleration : air_acceleration;
        vel.X = Mathf.MoveToward(vel.X, 0.0f, acceleration * (float)delta);
        vel.Y += gravity * (float)delta;
        Velocity = vel;

        MoveAndSlide();
    }

    public void TickPhysics(int currentSate, double delta)
    {
        switch (currentSate)
        {
            case (int)State.IDLE:
                Move(defaultGravity,delta);
                break;

            case (int)State.RUNNING:
                Move(defaultGravity, delta);
                break;
            case (int)State.JUMP:
                Move(isFirstFrame? 0 :defaultGravity, delta);
                break;

            case (int)State.FALL:
                Move(defaultGravity, delta);
                break;

            case (int)State.LANDING:
                Stand(defaultGravity, delta);
                break;

        }
        isFirstFrame = false;
    }

    public void TransitionSate(int from, int to)
    {
        if(!GROUD_STATE.Contains(from) && GROUD_STATE.Contains(to)) //一旦着陆，停止郊狼时间
        {
            coyoteTimer.Stop();
        }

        switch (to)
        {
            case (int)State.IDLE:
                animationPlayer.Play("idle");
                break;

            case (int)State.RUNNING:
                animationPlayer.Play("running");
                break;
            case (int)State.JUMP:
                animationPlayer.Play("jump");
                var vel = Velocity;
                vel.Y = jumpVelocity;
                coyoteTimer.Stop();
                jumpRequestTimer.Stop();
                Velocity = vel;
                break;

            case (int)State.FALL:
                animationPlayer.Play("fall");
                if (GROUD_STATE.Contains(from)) //下落时开启郊狼时间
                {
                    coyoteTimer.Start();
                }
                break;
            case (int)State.LANDING:
                animationPlayer.Play("landing");
                break;
        }
        isFirstFrame = true;
    }

    public int GetNextState(int currentSate)
    {
        var dir = Input.GetAxis("move_left", "move_right");
        var isStill = Mathf.IsZeroApprox(dir) && Mathf.IsZeroApprox(Velocity.X); //判断是否站立不动
        var isJump = (IsOnFloor() || coyoteTimer.TimeLeft > 0) && jumpRequestTimer.TimeLeft > 0; //判断是否跳起

        if (isJump) return (int)State.JUMP;
        switch (currentSate)
        {
            case (int)State.IDLE:
                if (!isStill)
                    return (int)State.RUNNING;
                return (int)State.IDLE;

            case (int)State.RUNNING:
                if (isStill)
                    return (int)State.IDLE;
                break;
            case (int)State.JUMP:
                if (Velocity.Y >= 0)
                    return (int)State.FALL;
                break;

            case (int)State.FALL:
                if (IsOnFloor())
                    return isStill ? (int)State.LANDING : (int)State.RUNNING;
                break;
            case (int)State.LANDING:
                if (!animationPlayer.IsPlaying())
                    return (int)State.IDLE;
                break;
        }
        return currentSate;
    }

}

